//
//  QQProtoObject.h
//  QQNetworkEngine
//
//  Created by elvisgao on 2020/08/24.
//  Copyright © 2020 tencent. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "QQModelObject.h"
#import "QQNetworkObjectProtocol.h"

NS_ASSUME_NONNULL_BEGIN

/// QQProtoObject相关的错误域
OBJC_EXPORT NSErrorDomain const QQProtoErrorDomain;

NS_ERROR_ENUM(QQProtoErrorDomain) {
    QQProtoErrorUnknown = -1,
    QQProtoErrorInvalidParam = -10,
    QQProtoErrorPbCodecEncode = -11,
    QQProtoErrorPbCodecDecode = -12,
    QQProtoErrorInvalidMeta = -13,
    QQProtoErrorInvalidValue = -14,
    QQProtoErrorCodecNumber = -15,
    QQProtoErrorCodecArray = -16,
    QQProtoErrorCodecObject = -17,
    QQProtoErrorUnsupportProperty = -18,
};

OBJC_EXPORT NSErrorUserInfoKey const QQProtoErrorClassKey;



@protocol QQProtoObject <NSObject>

@optional

- (BOOL)decodeWithBuffer:(const void*)buffer size:(uint64_t)size error:(NSError **)outError;

@end

/**
 避免引入c++头文件而定义的类型，实际使用时传入的是类型是`CPBMessageDecoder *`
 */
typedef void * QQProtobufDecoderRef;

@interface QQProtoObject : QQModelObject <QQProtoObject, QQNetworkObjectProtocol>

/**
 * @brief 请求的超时时间，单位s， 默认值30s
 */
@property (nonatomic, assign) int timeout;

/**
 * 将当前对象打包成protobuf数据
 * @return 打包后的数据，如果打包出错，会返回nil
 */
- (NSData * __nullable)encode;

/**
 * 将当前对象打包成protobuf数据
 * @param error 打包出错时会在这里返回错误
 * @return 打包后的数据，如果打包出错，会返回nil
 */
- (NSData * __nullable)encodeWithError:(out NSError **)error;

/**
 从decoder解包数据。子类`必须`重写

 @param decoder 实际类型为`CPBMessageDecoder *`，使用opaque type是为了避免引入c++头文件
 @param error 解包的错误
 @return 是否解码成功
 */
- (BOOL)decodeWithDecoder:(QQProtobufDecoderRef)decoder error:(NSError **)error;

/**
 解包数据，填入相应字段
 
 @param data protobuf数据
 @return 是否解包成功
 */
- (BOOL)decodeWithData:(NSData *)data;

- (BOOL)decodeWithData:(NSData *)data error:(out NSError **)error;

/**
 解包数据，生成相应子类的实例
 
 @param decoder 实际类型为`CPBMessageDecoder *`，使用opaque type是为了避免引入c++头文件
 @param error 解包的错误
 @return 相应pb类型的实例
 */
+ (instancetype __nullable)objectWithDecoder:(QQProtobufDecoderRef)decoder error:(out NSError **)error;

@end

#pragma mark - 工具生成pb属性时使用到的宏
/**
 pb协议属性动态化
*/
#ifndef PB_USE_DYNAMIC_PROPERTY
#define PB_USE_DYNAMIC_PROPERTY 1
#endif

#define PB_STRINGIFY(str)  PB_STRINGIFY_(str)
#define PB_STRINGIFY_(str) # str

#define PB_PROP_PREFIX @"pb__"
#define PB_PROP_ATTR_GETTER_SETTER(name) getter=pb_##name, setter=setPb_##name:
// 普通pb命名格式为pb__tag__name 例子：pb__1__appId
#define PB_PROP_NM(tag, name) pb__##tag##__##name
// 数组pb命名格为pb__tag__name__subClassType 例子：pb__1__whiteList__NSString
#define _PB_PROP_ARRAY_NM(tag, name, className) pb__##tag##__##name##__##className
#define PB_PROP_ARRAY_NM(tag, name, className) _PB_PROP_ARRAY_NM(tag, name, className)
// 字典pb命名格式为pb__tag__name__keyClassType__valueClassType 例子：pb__1__table__NSString__NSString
#define _PB_PROP_DICT_NM(tag, name, keyClassName, valueClassName) pb__##tag##__##name##__##keyClassName##__##valueClassName
#define PB_PROP_DICT_NM(tag, name, keyClassName, valueClassName) _PB_PROP_DICT_NM(tag, name, keyClassName, valueClassName)
#define PB_PROP_COMPONENT_SEPERATOR @"__"

#define PB_PROP_NM_COMPONENT_COUNT 3
#define PB_PROP_ARRAY_NM_COMPONENT_COUNT 4
#define PB_PROP_DICT_NM_COMPONENT_COUNT 5
#define PB_PROP_NM_INDEX_TAG 1 // tag的index
#define PB_PROP_ARRAY_NM_INDEX_SUBCLASSTYPE 3 // 数组里面数据类型的index
#define PB_PROP_DICT_NM_KEY_SUBCLASSTYPE 3 // 字典中key数据类型的index
#define PB_PROP_DICT_NM_VALUE_SUBCLASSTYPE 4 // 字典中value数据类型的index

#define PB_PROP_ASSIGN(tag, name, type) @property(nonatomic, assign, PB_PROP_ATTR_GETTER_SETTER(name)) type PB_PROP_NM(tag, name)
#define PB_PROP_STRONG(tag, name, type) @property(nonatomic, strong, PB_PROP_ATTR_GETTER_SETTER(name)) type *PB_PROP_NM(tag, name)
#define PB_PROP_ARRAY(tag, name, subType) @property(nonatomic, strong, PB_PROP_ATTR_GETTER_SETTER(name)) NSArray <subType *> *PB_PROP_ARRAY_NM(tag, name, subType)
#define PB_PROP_NUMARRAY(tag, name, subType) @property(nonatomic, strong, PB_PROP_ATTR_GETTER_SETTER(name)) NSArray <NSNumber *> *PB_PROP_ARRAY_NM(tag, name, subType)


#define PB_PROP_DICT(tag, name, keyType, valueType) @property(nonatomic, strong, PB_PROP_ATTR_GETTER_SETTER(name)) NSDictionary <keyType *, valueType *> *PB_PROP_DICT_NM(tag, name, keyType, valueType)
#define PB_PROP_DICT_NUMKEY(tag, name, keyType, valueType) @property(nonatomic, strong, PB_PROP_ATTR_GETTER_SETTER(name)) NSDictionary <NSNumber *, valueType *> *PB_PROP_DICT_NM(tag, name, keyType, valueType)
#define PB_PROP_DICT_NUMVALUE(tag, name, keyType, valueType) @property(nonatomic, strong, PB_PROP_ATTR_GETTER_SETTER(name)) NSDictionary <keyType *, NSNumber *> *PB_PROP_DICT_NM(tag, name, keyType, valueType)
#define PB_PROP_DICT_NUMKEYVALUE(tag, name, keyType, valueType) @property(nonatomic, strong, PB_PROP_ATTR_GETTER_SETTER(name)) NSDictionary <NSNumber *, NSNumber *> *PB_PROP_DICT_NM(tag, name, keyType, valueType)

NS_ASSUME_NONNULL_END
